针对大屏设备优化 Android 应用的方式及相关注意事项
近年来,包括大型可折叠设备、平板电脑以及 Chromebook 等大屏 Android 设备的数量与日俱增。确保应用可以在大屏设备上为用户提供无缝体验比以往任何时候都更加重要。例如,用户希望应用能够更充分利用这些设备的更大屏幕空间。我们发现,如果应用能够支持大屏设备,便可以在这些设备上实现更出色的业务指标。
在这些设备上实现更出色的业务指标
https://developer.android.google.cn/large-screens/stories
这些设备还可以在不同地方以及不同的方式来使用,而不是通常预想的手机形式。例如,可折叠设备可在桌面模式下使用,用户可能离桌面显示器有一定距离,并且可以搭配鼠标和键盘来使用大屏设备。
但伴随着这些新变化,也出现了一些需要我们注意的新问题。例如:
当用户在平板电脑上使用应用时,是否可以通过双手来便捷的触到最重要的控件? 用户能否通过键盘和鼠标使用应用的各项功能? 无论设备如何放置,应用相机预览的方向能否与设备屏幕方向一致?
大屏应用设计和应用质量
虚拟设备
https://developer.android.google.cn/studio/run/managing-avds
大屏设备 Material Design 指南
https://material.io/blog/material-design-for-large-screens规范化布局
https://m3.material.io/foundations/layout/canonical-layouts/overview大屏设备图库
https://developer.android.google.cn/large-screens/gallery大屏应用质量指南
https://developer.android.google.cn/docs/quality-guidelines/large-screen-app-quality
针对大屏设备进行优化
的注意事项
🙅 错误做法: 假设应用享有对资源的专属访问权限
不要假设您的应用享有对相机等硬件资源的专属访问权限。通常情况下,大屏设备上会有多款应用同时处于活跃状态,其他应用也可能会尝试访问相同的资源。 因此,您应该测试您的应用与其他应用一起工作时的表现。永远不要假设某一资源在任意给定时间均可使用。
对相机等硬件资源的专属访问权限
https://developer.android.google.cn/training/camera2/camera-preview#exclusive_resources
🙆 正确做法: 妥善处理硬件访问权限
在尝试使用相机等硬件资源之前,您需要先检查此类资源。请记住,硬件外设可以随时通过 USB 添加和移除 如果应用在运行时无法访问给定资源,可以妥善处理失败的情况
try {
// Attempt to use the camera
...
} catch (e: CameraAccessException) {
e.message?.let { Log.e(TAG, it) }
// Fail gracefully if the camera isn't currently available
}
}
🙆 正确做法: 对生命周期事件做出恰当的响应
在 onPause() 期间,应用可能仍然对用户可见,特别是当屏幕上有多款应用更是如此。因此,在调用 onStop() 之前,您需要保持媒体播放和界面处于活跃状态。
🙅 错误做法: 在 onPause() 期间停止应用界面
override fun onPause() {
//DON'T clean up resources here.
//Your app can still be visible.
super.onPause()
}
在过去,应用的常用模式是利用屏幕宽度以创建类似 "isTablet" 这样的布尔值,从而根据运行应用的设备类型做出调整,但这种方法很不可靠。应用需要使用代理来确定设备类型是这种方法的核心问题,而此类代理容易出错。例如,应用在启动时检测到大显示屏,因此确定该设备是平板电脑。但是,如果应用为了不占据全屏而调整其窗口大小,则可能会出现异常。即使您使用的设备类型布尔值可以响应配置更改,在展开可折叠设备的情况下也会影响用户体验,因为在其他配置发生更改 (如重新折叠设备) 之前,该布尔值无法返回。
如果您使用设备类型布尔值来调整布局,可以改为使用 WindowSizeClass。该内容库同时支持视图和 Jetpack Compose,以便您能够简单明确地根据预定义断点调整界面。
// androidx.compose.material3.windowsizeclass.WindowSizeClass
class MainActivity : ComponentActivity() {
…
setContent {
val windowSizeClass = calculateWindowSizeClass(this)
WindowSizeClassDisplay(windowSizeClass)
}
@Composable
fun WindowSizeClassDisplay(windowSizeClass : WindowSizeClass) {
when (windowSizeClass.widthSizeClass)
{
WindowWidthSizeClass.Compact -> { compactLayout() }
WindowWidthSizeClass.Medium -> { mediumLayout() }
WindowWidthSizeClass.Expanded -> { expandedLayout() }
}
}
视图
https://developer.android.google.cn/reference/kotlin/androidx/compose/material3/windowsizeclass/WindowSizeClass
如果您使用 isTablet 来更改面向用户的字符串 (如 "您的平板电脑"),那么您可能不需要更多信息便可以轻松解决此问题。您只需使用诸如 "您的 Android 设备" 等更通用的措辞即可。
如果您使用设备类型布尔值来预测电话、蓝牙等硬件功能或资源是否存在,在尝试使用所需功能之前,您可以在运行时直接检查其是否可用,如果功能不可用,则需要妥善处理失败的情况。这种基于功能的方法可确保应用能够恰当地响应可连接或移除的外设。此外,这种方法还可以避免某种功能即使受设备支持,也依然缺失的情况。
val packageManager: PackageManager = context.packageManager
val hasTelephony =
packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
显示相机预览可能极为复杂,这涉及到屏幕方向、宽高比等。在您使用 Jetpack CameraX 时,该库将为您处理尽可能多的细节:
https://developer.android.google.cn/training/camerax
🙅 错误做法: 假设相机预览的方向会与设备屏幕方向保持一致
在应用中实现相机预览时,需要考虑几种方向: 自然屏幕方向、设备屏幕方向和显示屏幕方向。如要正确实现相机预览,您需要考虑这几种方向,并随着设备条件的变化做出调整。
考虑几种方向
https://chromeos.dev/en/android/camera-orientation#all-about-orientations
🙅 错误做法: 假设宽高比是静态的
很多原因都可能导致应用可用窗口的宽高比发生变化。您需要确保相机预览应能够适应不断变化的宽高比。
应用可用窗口的宽高比发生变化
https://developer.android.google.cn/training/camera2/camera-preview#aspect_ratio
在声明应用的功能要求时,请参阅《针对大屏设备的实战宝典》中的指南。为了确保您没有不必要地限制应用的受众群体,您需要使用适用于您应用的最详尽的清单条目。
<uses-feature android:name="android.hardware.camera.any" android:required="false" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-feature android:name="android.hardware.camera.flash" android:required="false" />
针对大屏设备的实战宝典
https://developer.android.google.cn/guide/topics/large-screens/large-screen-cookbook
大屏设备的屏幕经常变化,包括其 WindowInsets。因此,请勿只在应用启动时才检查边衬区,而且从不进行更改。
当边衬区发生变化时,WindowInsetsListener API 会通知应用
ViewCompat.setOnApplyWindowInsetsListener(view) { view, windowInsets ->
val insets = windowInsets.getInsets(
WindowInsetsCompat.Type.systemBars())
view.updateLayoutParams<MarginLayoutParams>(
leftMargin = insets.left,
bottomMargin = insets.bottom,
rightMargin = insets.right,
)
WindowInsetsCompat.CONSUMED
}
windowInsetsPadding Modifier 会根据给定的窗口边衬区类型动态填充。此外,该 Modifier 的多个实例可以相互通信,以避免添加重复的填充,而且这些实例会自动进行动画处理。
windowInsetsPadding Modifier
https://youtu.be/mlL6H-s0nF0
越来越多的用户开始借助鼠标和键盘来使用 Android 应用,但有时您的应用可能无法提供触摸支持,例如当该应用在连接的显示器上显示时。您需要确保应用的所有功能都支持基本的鼠标或触控板互动:
https://www.youtube.com/watch?v=ucaSqyfpPas
为了确保您的应用在大屏设备上拥有出色体验,最重要的是要亲自测试应用。我们为您准备好了一个严格测试计划——大屏设备兼容性测试,欢迎您即刻尝试。
大屏设备兼容性测试
https://developer.android.google.cn/docs/quality-guidelines/large-screen-app-quality#large_screen_compatibility_tests
Android Studio 提供了可在开发过程中使用的工具,帮助您更轻松地针对大屏设备优化应用。例如,您可以使用 MultiPreview 注释以同时在多种条件下直观地查看应用。此外,Android 虚拟设备管理器还提供多种平板电脑、桌面设备和可折叠设备的 Android 虚拟设备 (AVD),以便您即刻在大屏设备上测试应用。
MultiPreview 注释
https://developer.android.google.cn/jetpack/compose/tooling/previews#preview-multipreviewAndroid 虚拟设备管理器
https://developer.android.google.cn/studio/run/managing-avds
Google I/O 大会精彩回顾
希望这些建议可作为您针对大屏设备优化应用时的良好起点。在今年的 Google I/O 大会上,我们发布了 Google 的最新资讯和创新。您可以查看我们的精彩回顾内容合集获取实用的最新产品动态。也欢迎您持续关注 "Android 开发者" 微信公众号,及时了解更多开发技术和产品更新。
Google I/O 大会
https://io.google/2023/program/
推荐阅读